昨天講了中介層的觀念,今天我們來實作中介層!
中介層通常會以一個類別封裝起來,再由擴充方法曝露。假設現在有個需求,要透過 URL 中一個區塊來決定語系。範例程式碼可以參考這邊。
| URL | 語系 | 
|---|---|
| / | zh-TW | 
| /en-us/ | en-US | 
| /de-de/ | de-DE | 
直接將中介層邏輯寫在 Startup.Configure 方法中:
public class Startup
{
    private static readonly string[] EnabledCultures = new[] {"zh-tw", "en-us", "de-de"};
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.Use((context, next) =>
        {
            var path = context.Request.Path;
            var match = Regex.Match(path, $"^/({string.Join('|', EnabledCultures)})/CultureMiddleware");
            var defaultCulture = "zh-tw";
            if (match.Success)
            {
                defaultCulture = match.Groups[1].Value;
                context.Request.Path = new PathString("/CultureMiddleware/Index");
            }
            var culture = new CultureInfo(defaultCulture);
            CultureInfo.CurrentCulture = culture;
            CultureInfo.CurrentUICulture = culture;
            // Call the next delegate/middleware in the pipeline
            return next();
        });
        app.Run(async context =>
        {
            await context.Response.WriteAsync(
                $"你的語系是:{CultureInfo.CurrentCulture.DisplayName}",
                Encoding.UTF8);
        });
    }
}
這個範例中,EnabledCultures 變數定義應用程式中支援的語系。
private static readonly string[] EnabledCultures = new[] {"zh-tw", "en-us", "de-de"};
利用正規表示式來搜尋 URL 中的語系區塊,如果找不到會預設為 zh-tw。同時把要求的路徑指定為 /CultureMiddleware/Index,否則 router 會認錯 Controller。
var match = Regex.Match(path, $"/({string.Join('|', EnabledCultures)})");
var defaultCulture = "zh-tw";
if (match.Success)
{
    defaultCulture = match.Groups[1].Value;
    context.Request.Path = new PathString("/CultureMiddleware/Index");
}
最後設定應用程式中的語系。
var culture = new CultureInfo(defaultCulture);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
不同 URL 顯示的結果:
一般習慣會把中介層的邏輯封裝成一個類別,範例程式碼可以參考這邊。
把委任封裝成類別:
public class RequestCultureMiddleware
{
    private static readonly string[] EnabledCultures = new[] { "zh-tw", "en-us", "de-de" };
    private readonly RequestDelegate _next;
    public RequestCultureMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    public async Task InvokeAsync(HttpContext context)
    {
        var path = context.Request.Path;
        var match = Regex.Match(path, $"/({string.Join('|', EnabledCultures)})");
        var defaultCulture = "zh-tw";
        if (match.Success)
        {
            defaultCulture = match.Groups[1].Value;
            context.Request.Path = new PathString("/CultureMiddleware/Index");
        }
        var culture = new CultureInfo(defaultCulture);
        CultureInfo.CurrentCulture = culture;
        CultureInfo.CurrentUICulture = culture;
        // Call the next delegate/middleware in the pipeline
        await _next(context);
    }
}
透過 IApplicationBuilder 曝露中介層:
public static class RequestCultureMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestCulture(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestCultureMiddleware>();
    }
}
在 Startup.Configure 中設定中介層:
public void Configure(IApplicationBuilder app)
{
    app.UseRequestCulture();
    app.Run(async context =>
    {
        await context.Response.WriteAsync(
            $"你的語系是:{CultureInfo.CurrentCulture.DisplayName}",
            Encoding.UTF8);
    });
}